# coding:utf8
import requests
import redis
import json
import copy
import re
from lxml import etree
from bs4 import BeautifulSoup

class Spider(object):
    """exemple:
       spider = Spider(username='admin', password='123456')
       data = spider.get_all_data()"""
    def __init__(self, **kwargs):
        self.db_key = 'oa_conn'
        self.userId = kwargs.has_key('username') and kwargs['username']
        self.userPwd = kwargs.has_key('password') and kwargs['password']
        assert self.userId and self.userPwd, \
            'Spider(username="xxx", password="xxx")'
        print self.userId, self.userPwd
        self.pool = redis.ConnectionPool(host='127.0.0.1', port=6379, db=0)
        self.origin_url = 'http://demo.oa8000.com/'
        self.base_url = '{}OAapp/WebObjects/OAapp.woa'.format(self.origin_url)
        self.get_data_url_post = ('{}OAapp/bfapp/buffalo/portal'
                                  'Service').format(self.origin_url)
        self.login_url_post = ('{}/wo/com.oa8000.mainapp.Main/4Wa28aXv2YgE'
                               'r8HHUysXW0/0.11').format(self.base_url)
        self.iframe_url_get = ('{}OAapp/dir20170713013507205/page/CN/'
                               'PorIndex000001415X.jsp').format(self.origin_url)
        self.info_url = ('{}OAapp/dir20170713013507205/page/CN/'
                         'PorIndex000001415X.jsp').format(self.origin_url)
        self.rs = requests.Session()
        self.headers = {
            'User-Agent': ('Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/'
                           'MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) '
                           'Chrome/59.0.3071.115 Mobile Safari/537.36')
        }
        self.rs.headers.update({'User-Agent': self.headers['User-Agent']})
        self.login_data = {
            'userId': self.userId,
            'userPwd': self.userPwd,
            'functionName': 'LOGIN',
            'screenWidth': '320',
            'screenHeight': '553',
            '11.16': 'BLUE',
            '11.18': 'CN',
            '11.20': 'BLUE',
            'wosid': '4Wa28aXv2YgEr8HHUysXW0',
        }
        self.task_map = {
            'newwaitTask.list': {
                'title': '代办事项',
                'ntype': 'msgType',
                '1': ['待我审批', 'dwsp'],
                '2': ['待我阅读', 'dwyd'],
                '3': ['短消息', 'dxx']
            },
            'newallNews.list': {
                'title': '公司动态',
                'ntype': 'msgType',
                '3': ['新闻', 'xw'],
                '2': ['通知', 'tz'],
                '4': ['公告', 'gg']
            },
            'newrecentFile.list': {
                'title': '近期文档',
                'ntype': 'fileType',
                '1': ['知识管理', 'ssgx'],
                '4': ['规章制度', 'gzzd'],
                '6': ['个人文件柜', 'grwjg']
            },
            'newmyMeetingIndex.list': {
                'title': '我的会议',
                'ntype': ''
            }
        }
        self.get_data_origin = '''
            <buffalo-call>
            <method>getData</method>
            <string>web</string>
            <string>%s</string>
            <string></string>
            <string></string>
            <string></string>
            <string>%s</string>
            <string>%s</string>
            <string>%s</string>
            <string>%s</string>
            <string>{ maxRecordCount : ["%s"], msgType : ["%s"], fileType : ["%s"], sqlId : [""], sqlStr : [""], columnAry : %s, formatAry : %s, lengthAry : %s, languageType : ["CN"] }</string>
            </buffalo-call>
        '''
        self.info = {}
        self.get_data = copy.copy(self.get_data_origin)
        try:
            self.login()
            self.test('gsdt')
        except Exception, e:
            self.del_redis_by_name(self.db_key)
            self.get_data = copy.copy(self.get_data_origin)
            self.login()
            self.get_infomation()

    def login(self):
        """获取session值并激活会话登录状态"""
        data = self.get_data_from_redis()
        if data:
            data = json.loads(data.replace('\'', '"'))
        if data and data.has_key('sessionId') and data.has_key('tokenId') \
                and data['sessionId'] and data['tokenId']:
            self.tokenId = data['tokenId']
            self.sessionId = data['sessionId']
            self.htoa8000V71 = data['htoa8000V71']
            cj = requests.cookies.RequestsCookieJar()
            cj.set('JSESSIONID', self.sessionId,
                   domain='demo.oa8000.com', path='/OAapp')
            cj.set('htoa8000V71', self.htoa8000V71,
                   domain='demo.oa8000.com', path='/')
            self.rs.cookies = cj
            #self.rs.headers.update({'JSESSIONID': self.sessionId,
            #                        'htoa8000V71': self.htoa8000V71})
        else:
            xx = self.rs.get(self.base_url)
            soup = BeautifulSoup(xx.text, 'lxml')
            htoa = [self.userId]
            htoa.append(soup.find('input', {'id': 'useStyle'})['value'])
            htoa.append(soup.find('input', {'id': 'useLanguage'})['value'])
            htoa.append(soup.find('input', {'id': 'useStyleType'})['value'])
            htoa.append('')
            self.htoa8000V71 = '|'.join(htoa)
            self.sessionId = self.rs.cookies.get('JSESSIONID')
            self.rs.post(self.login_url_post, data=self.login_data)
            self.get_user_token()
            self.save_to_redis()

    def get_user_token(self):
        """获取用户token"""
        text = self.rs.get(self.iframe_url_get).text
        xx = etree.HTML(text)
        self.tokenId = xx.xpath('//input[@id="traceUserToken"]')[0].get('value')

    def save_to_redis(self):
        """将sessionid和tokenid存储到redis数据库"""
        cor = redis.Redis(connection_pool=self.pool)
        cor.hset(self.db_key, self.userId, {'sessionId': self.sessionId,
                                            'tokenId': self.tokenId,
                                            'htoa8000V71': self.htoa8000V71})

    def del_redis_by_name(self, name):
        cor = redis.Redis(connection_pool=self.pool)
        cor.delete(name)

    def get_data_from_redis(self):
        """返回sessionid和tokenid的值
           数据格式如{'sessionId': 'xxx', 'tokenId': 'xxx'}"""
        cor = redis.Redis(connection_pool=self.pool)
        return cor.hget(self.db_key, self.userId)

    def xml_to_dict(self, xml, start=0):
        """作用：将xml元素转换成对象
           处理的xml对应格式：<parent><key></key><value></value>...</parent>"""
        tmp_all_child = xml.getchildren()[start:]
        assert len(tmp_all_child) % 2 == 0, "传入的xml元素子节点个数为奇数"
        tmp_parent = tmp_all_child[0::2]
        tmp_child = tmp_all_child[1::2]
        ans = {}
        for i in range(len(tmp_parent)):
            text = tmp_child[i].text
            if type(text) == type(u'unicode'):
                text = text.encode('utf8')
            ans[tmp_parent[i].text] = text
        return ans

    def get_all_data(self, title=None):
        """作用：返回指定类别块的数据
           调用方式：self.get_data_by_name('dbsx')
           name：为data_map的各一级属性值，中文含义见其name属性
           返回数据格式：[data1, data2, ...]"""
        ans = {}
        for name in self.info:
            if name not in self.task_map or not self.task_map[name]['ntype']:
                continue
            for item in self.info[name]:
                if title and self.task_map[name][item[self.task_map[name]['ntype']]][1] != title:
                    continue
                data = self.get_data % (self.tokenId, self.userId, name,
                                        self.userId, self.deptId,
                                        item['maxRecordCount'],
                                        item['msgType'], item['fileType'],
                                        json.dumps(item['columnName']),
                                        json.dumps(item['format']),
                                        json.dumps(item['length']))
                text = self.rs.post(self.get_data_url_post, data=data).content
                data_xml = etree.XML(text)
                maps = data_xml.xpath('//buffalo-reply/list/map')
                if len(maps) == 0:
                    continue
                task_name = self.task_map[name][item[self.task_map[name]['ntype']]]
                ans[task_name[1]] = []
                ans[task_name[1]].append(task_name[0])
                for m in maps:
                    result = self.xml_to_dict(m, start=1)
                    result['functionId'] = name
                    result['parent_title'] = self.task_map[name]['title']
                    result['title'] = task_name[0]
                    result['type'] = item[self.task_map[name]['ntype']]
                    ans[task_name[1]].append(result)
        return ans

    def test(self, name):
        self.get_infomation()
        self.get_all_data('tz')

    def get_all_data_bak(self):
        alldata = {}
        for key in self.data_map:
            alldata[key] = {}
            alldata[key]['name'] = self.data_map[key]['name']
            alldata[key]['data'] = self.get_data_by_name(key)
        return alldata

    def get_infomation(self):
        cont = self.rs.get(self.info_url).content
        soup = BeautifulSoup(cont, 'lxml')
        configs = soup.find_all('div', {'class': 'Config'})
        assert configs, '详情页无数据，请重新登录'
        self.userId = re.findall('var userId.*;', cont)[0].split('=')[1][2:-2]
        self.deptId = re.findall('var deptId.*;', cont)[0].split('=')[1][2:-2]
        config_attr1 = ['maxRecordCount', 'msgType',
                        'fileType', 'functionId']
        config_attr2 = ['columnName', 'format', 'length']
        for config in configs:
            tmp_obj = {}
            for ca in config_attr1:
                attrname = None
                if config.has_attr(ca):
                    attrname = ca
                elif config.has_attr(ca.lower()):
                    attrname = ca.lower()
                tmp_obj[ca] = config[attrname] if attrname else ''
            sps = config.find_all('span', {'functionId': tmp_obj['functionId']})
            if not sps:
                sps = config.find_all('span', {'functionid': tmp_obj['functionId']})
            for sp in sps:
                for ca in config_attr2:
                    if not tmp_obj.has_key(ca):
                        tmp_obj[ca] = []
                    attrname = None
                    if sp.has_attr(ca):
                        attrname = ca
                    elif sp.has_attr(ca.lower()):
                        attrname = ca.lower()
                    tmp_obj[ca].append(sp[attrname] if attrname else '')
            if not self.info.has_key(tmp_obj['functionId']):
                self.info[tmp_obj['functionId']] = []
            self.info[tmp_obj['functionId']].append(tmp_obj)

    def get_detail(self, functionId, id, ntype=None):
        href = ''
        # 近期文档
        if functionId.find("recentFile.list") > -1 or\
           functionId.find("newrecentFile.list") > -1 or\
           functionId in ["dMagazineFile.list1", "knowledgemg.list",
                          "downloadfile.list", "rulesfile.list",
                          "personalfiles.list", "leastuploadfiles.list"]:
            href = "%s/wa/Portal?page=HtFile0131&fileId=%s&functionId=%s" \
                % (self.base_url, id, functionId)
        # 待办事项
        if functionId.find('newwaitTask.list') > -1:
            if ntype == '1':
                href = ('%s/wa/Portal?page=actOpenWaitTrace&'
                        'traceInstanceIndexId=%s') % (self.base_url, id)
            elif ntype == '2':
                href = ('%s/wa/Portal?page=actOpenTraceHandOut&'
                        'traceHandOutId=%s') % (self.base_url, id)
            elif ntype == '3':
                href = '%s/wa/Portal?page=HtMsg0111&msgId=%s&functionId=%s' \
                    % (self.base_url, id, functionId)
        # 领导信箱查看
        if functionId in ["leaderMail.list", "newleaderMail.list"]:
            href = ('%s/wa/Portal?page=HtLeadership0106&leaderMailId=%s&'
                    'functionId=%s') % (self.base_url, id, functionId)
        # 公司动态
        if functionId in ["allNews.list", "newallNews.list"]:
            href = '%s/wa/Portal?page=HtMsg0111&msgId=%s&functionId=%s' \
                % (self.base_url, id, functionId)
        text = self.rs.get(href).text
        soup = BeautifulSoup(text, 'lxml')
        pageid = soup.find('input', {'id': 'pageId'})['value']
        actform = soup.select('#actForm input')
        datas = {}
        for item in actform:
            if not item.has_attr('name'):
                continue
            datas[item['name']] = item['value'] if item.has_attr('value') else ''
        href_new = self.origin_url + 'OAapp/' + pageid
        text = self.rs.post(href_new, data=datas).text
        soup = BeautifulSoup(text, 'lxml')
        #center = soup.find('center')
        center = soup.find('input', {'id': 'contentMemo'})['value']
        center = BeautifulSoup(center, 'lxml')
        [i.decompose() for i in center.find_all('img')]
        return str(center)


if __name__ == '__main__':
    spider = Spider(username='liuxiaoyu', password='123456')
    href = spider.get_detail('newwaitTask.list', 'TraceI013652EC47', '1')
    data = spider.get_all_data()
    data = json.dumps(data, False, False)
